/********************************************************************
	
	Tau Virtual Machine
	
	boolean.c
	
	Copyright (c) 2009 Tau Virtual Machine Project
	
	See COPYING.txt for copying and redistribution conditions.
	
	Author:  Matt C.
	Date:  Aug 05, 2009
	
********************************************************************/

#include <stdlib.h>
#include "tau_types.h"

// ROUTINES *********************************************************

TauValue* TauMakeBoolean (int i)
{
	TauValue* v;
	
	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_BOOLEAN;
	v->refs = 0;
	v->data.i = i;
	
	return v;
}

// Negate ***********************************************************

TauValue* TauBooleanNegate (TauValue* vs)
{
	// - B -> I
	
	return TauMakeInteger(-vs->data.b);
}

// Add **************************************************************

TauValue* TauBooleanAdd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// B + B -> I
	// B + I -> I
	// B + R -> R
	// B + C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.b + vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.b + vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			r = (double)(vs->data.b) + vt->data.r;
			vd = TauMakeReal(r);
			break;
		
		case T_COMPLEX:
			cr = (double)(vs->data.b) + vt->data.c->real;
			ci = vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;
		
		default:
			// exception
			break;
	}

	return vd;
}

// Subtract *********************************************************

TauValue* TauBooleanSubtract (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// B - B -> I
	// B - I -> I
	// B - R -> R
	// B - C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.b - vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.b - vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			r = (double)(vs->data.b) - vt->data.r;
			vd = TauMakeReal(r);
			break;
		
		case T_COMPLEX:
			cr = (double)(vs->data.b) - vt->data.c->real;
			ci = -vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;

		default:
			// exception
			break;
	}

	return vd;
}

// Multiply *********************************************************

TauValue* TauBooleanMultiply (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// B * B -> I
	// B * I -> I
	// B * R -> R
	// B * C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.b * vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.b * vt->data.b;
			vd = TauMakeInteger(i);
			break;

		case T_REAL:
			r = (double)(vs->data.b) * vt->data.r;
			vd = TauMakeReal(r);
			break;
		
		case T_COMPLEX:
			d = (double)(vs->data.b);
			cr = d * vt->data.c->real;
			ci = d * vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;

		default:
			// exception
			break;
	}

	return vd;
}

// Divide ***********************************************************

TauValue* TauBooleanDivide (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	double numr, numi, den;
	double x, y;
	
	// B / B -> R
	// B / I -> R
	// B / R -> R
	// B / C -> C
	
	switch (vt->type)
	{
		case T_INTEGER:
			if (vt->data.i == 0)
				; // exception - divide by zero
			else
			{
				i = (double)(vs->data.b) / vt->data.i;
				vd = TauMakeInteger(i);
			}
			break;
		
		case T_BOOLEAN:
			if (!vt->data.b)
				; // exception - divide by zero
			else
			{
				i = (double)(vs->data.b) / vt->data.b;
				vd = TauMakeInteger(i);
			}
			break;

		case T_REAL:
			if (vt->data.r == 0.0)
				; // exception - divide by zero
			else
			{
				d = (double)(vs->data.b) / vt->data.r;
				vd = TauMakeReal(r);
			}
			break;
		
		case T_COMPLEX:
			d = (double)(vs->data.b);
			
			// vt format: x + jy
			x = vt->data.c->real;
			y = vt->data.c->imag;
			den = x*x + y*y;
			numr = d * x;
			numi = d * -y;
			cr = numr/den;
			ci = numi/den;
			
			cr = d * vt->data.c->real;
			ci = d * vt->data.c->imag;
			vd = TauMakeComplex(cr, ci);
			break;

		default:
			// exception
			break;
	}

	return vd;
}

// Modulo ***********************************************************

TauValue* TauBooleanModulo (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// B % B -> I
	// B % I -> I
	// B % R -> X
	// B % C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.b % vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.b % vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}

	return vd;
}

// FloorDivide ******************************************************

TauValue* TauBooleanFloorDivide (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	double cr, ci;
	double r;
	int i;
	
	// B // B -> I
	// B // B -> I
	// B // R -> R
	// B // C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			if (vt->data.i == 0)
				; // exception - divide by zero
			else
			{
				i = vs->data.b / vt->data.i;
				vd = TauMakeInteger(i);
			}
			break;
		
		case T_BOOLEAN:
			if (!vt->data.b)
				; // exception - divide by zero
			else
			{
				i = vs->data.b / vt->data.b;
				vd = TauMakeInteger(i);
			}
			break;
		
		case T_REAL:
			// revert to normal division
			if (vt->data.r == 0.0)
				; // exception - divide by zero
			else
			{
				d = (double)(vs->data.b) / vt->data.r;
				vd = TauMakeReal(r);
			}
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// BitNot ***********************************************************

TauValue* TauBooleanBitNot (TauValue* vs)
{
	TauValue* vd;
	
	// ~ B -> I
	
	vd = TauMakeInteger(~vs->data.b);
	
	return vd;
}

// And **************************************************************

TauValue* TauBooleanAnd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// B & B -> I
	// B & I -> I
	// B & R -> X
	// B & C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.b & vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.b & vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Or ***************************************************************

TauValue* TauBooleanOr (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// B | B -> I
	// B | I -> I
	// B | R -> X
	// B | C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.b | vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.b | vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Xor **************************************************************

TauValue* TauBooleanXor (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int i;
	
	// B ^ B -> I
	// B ^ I -> I
	// B ^ R -> X
	// B ^ C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			i = vs->data.b ^ vt->data.i;
			vd = TauMakeInteger(i);
			break;
		
		case T_BOOLEAN:
			i = vs->data.b ^ vt->data.b;
			vd = TauMakeInteger(i);
			break;
		
		case T_REAL:
			// exception - undefined
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Not **************************************************************

TauValue* TauBooleanNot (TauValue* vs)
{
	TauValue* vd;
	
	// ! B -> B
	
	vd = TauMakeBoolean(!vs->data.b);
	
	return vd;
}

// Equal ************************************************************

TauValue* TauBooleanEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int cond1, cond2;
	int b;
	
	// B == B -> B
	// B == I -> B
	// B == R -> B
	// B == C -> B
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.b == vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.b == vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.b) == vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			cond1 = ((double)(vs->data.b) == vt->data.c->real);
			cond2 = (vt->data.c->imag == 0.0);
			b = (cond1 && cond2);
			vd = TauMakeBoolean(b);
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// NotEqual *********************************************************

TauValue* TauBooleanNotEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int cond1, cond2;
	int b;
	
	// B != B -> B
	// B != I -> B
	// B != R -> B
	// B != C -> B
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.b != vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.b != vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.b) != vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			cond1 = ((double)(vs->data.b) != vt->data.c->real);
			cond2 = (vt->data.c->imag != 0.0);
			b = (cond1 || cond2);
			vd = TauMakeBoolean(b);
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Greater **********************************************************

TauValue* TauBooleanGreater (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// B > B -> B
	// B > I -> B
	// B > R -> B
	// B > C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.b > vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.b > vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.b) > vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// Less *************************************************************

TauValue* TauBooleanLess (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// B < B -> B
	// B < I -> B
	// B < R -> B
	// B < C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.b < vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.b < vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.b) < vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// GreaterEqual *****************************************************

TauValue* TauBooleanGreaterEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// B >= B -> B
	// B >= I -> B
	// B >= R -> B
	// B >= C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.b >= vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.b >= vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.b) >= vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}
// LessEqual ********************************************************

TauValue* TauBooleanLessEqual (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	int b;
	
	// B <= B -> B
	// B <= I -> B
	// B <= R -> B
	// B <= C -> X
	
	switch (vt->type)
	{
		case T_INTEGER:
			b = (vs->data.b <= vt->data.i);
			vd = TauMakeBoolean(b);
			break;
		
		case T_BOOLEAN:
			b = (vs->data.b <= vt->data.b);
			vd = TauMakeBoolean(b);
			break;
		
		case T_REAL:
			b = ((double)(vs->data.b) <= vt->data.r);
			vd = TauMakeBoolean(b);
			break;
		
		case T_COMPLEX:
			// exception - undefined
			break;
		
		default:
			// exception
			break;
	}
	
	return vd;
}

// END **************************************************************



